<?php
package("Db_Adodb");
package("Query");
package("Utils");
/**
 * Encapsulate data about a class and its relationship to a table
 * 
 * TODO Below the wakeup and sleep methods take an object parameter.  It would
 * be best if there were a way to work around this.  After all the purpose of the
 * adapter class is to separate the object from this class.
 * 
 * The problem arises because several operations require the object's id.  The 
 * problem is that this class knows what the object's id is, but the adapter
 * has the object.
 * 
 * I think the solution is to create a method getKeyName or something similar, which
 * returns the name of the object's id.  Then the adapter can call the class and request
 * the id name, and use that in conjunction with the object to extract the real id.
 * The id could then be passed in as a parameter instead of the entire object.
 */
class Core_Class implements IteratorAggregate {
	
	/**
	 * Table name for this class
	 *
	 * @var String
	 */
	private $_table;
	
	/**
	 * Pointer to the id property for this class 
	 *
	 * @var int
	 */
	private $_id;

	/**
	 * Array of properties belonging to this class
	 *
	 * @var array
	 */
	private $_properties;
	
	/**
	 * @var Db_Common_Service
	 */
	private $_dbh;
	
	public function __construct($table) {
		$this->_table = $table;
		$this->_properties = array();
		$this->_dbh = new Db_Adodb_Service("mysql://Catch22:divein@localhost/test");
	}
	
	/**
	 * Add a new property to the class
	 *
	 * @param Core_Property $property
	 */
	public function bindProperty(Core_Property $property) {
		$this->_properties[] = $property;
	}
	
	/**
	 * Add the id to the class
	 *
	 * @param Core_Property $property
	 */
	public function bindKey(Core_Property $property) {
		$this->_properties[] = $property;
		$this->_id = count($this->_properties) - 1;
	}
	
	public function getIterator() {
		return new ArrayIterator($this->_properties);
	}
	
	/**
	 * Restore the object from a row in the table
	 *
	 * <p>First fetch the object's row from the table then loop through each
	 * property in the class and set its value with the corresponding column
	 * in the table.
	 */
	public function restore() {
		foreach ($this->select() as $result) { // confirm that there is only 1 result
			foreach ($this->_properties as $property) {
				$property->value = $result[$property->column];
			}
		}
	}
	
	/**
	 * Save the object's state to the database
	 * 
	 */
	public function save() {
		if (isset($this->_properties[$this->_id]->value)) {
			$this->update();
		} else {
			$this->_properties[$this->_id]->value = $this->insert();
		}
	}
	
	/**
	 * Delete the class' row from the table
	 */
	public function remove() {
		$this->delete();
	}
	
	private function delete() {
		$factory = Query_Factory::getSingleton();
		$query = $factory->construct("Query_Delete", array());
		$query->bindTable($this->_table);
		$this->buildQuery($query);
		$this->_dbh->save($query->toSql());
	}
	
	/**
	 * Perform an insert query on this class
	 */
	private function insert() {
		$factory = Query_Factory::getSingleton();
		$query = $factory->construct("Query_Insert", array());
		$query->bindTable($this->_table);
		$this->buildQuery($query);
		if ($this->_dbh->save($query->toSql())) {
			return $this->_dbh->lastInsertId();
		} // TODO do something meaningful on failure
	}
	
	/**
	 * Perform an update query on this class
	 */
	private function update() {
		$factory = Query_Factory::getSingleton();
		$query = $factory->construct("Query_Update", array());
		$query->bindTable($this->_table);
		$this->buildQuery($query);
		$this->_dbh->save($query->toSql());
	}
	
	/**
	 * Perform a select query on this class
	 *
	 * @return Db_Common_ResultSet
	 */
	private function select() {
		$factory = Query_Factory::getSingleton();
		$query = $factory->construct("Query_SelectOne", array());
		$query->bindTable($this->_table);
		$this->buildQuery($query);
		return $this->_dbh->select($query->toSql());
	}
	
	/**
	 * Loop through this class' properties and bind them to the given query
	 */
	private function buildQuery(Query_Builder & $query) {
		for ($i = 0; $i < count($this->_properties); $i++) {
			if ($i == $this->_id) {
				$query->bindKey($this->_properties[$i]);
			} else {
				$query->bindProperty($this->_properties[$i]);
			}
		}
	}
}
?>